use std::pin::Pin ;
use async_trait::async_trait ;
use serde_json::Value ;
use sled::Db ;

pub mod types {
    #[derive(Debug)]
    pub enum MinioReq {
        Ls(LsReq),
        Cp(CpReq),
        Cat(CatReq),
        Tee(TeeReq),
        Rm(RmReq),
        Mirror(MirrorReq),
        Sql(SqlReq),
    }
    #[derive(Debug)]
    pub struct MbReq {
        pub bucket: String,
    }
    #[derive(Debug)]
    pub struct LsReq {
        pub path: String,
    }
    #[derive(Debug)]
    pub struct CpReq {
        pub source: String,
        pub sink: String
    }
    #[derive(Debug)]
    pub struct CatReq {
        pub path: String
    }
    #[derive(Debug)]
    pub struct TeeReq {
        pub data: Vec<u8>,
        pub path: String,
    }
    #[derive(Debug)]
    pub struct RmReq {
        pub paths: Vec<String>,
    }
    #[derive(Debug)]
    pub struct ShareReq {
    }
    #[derive(Debug)]
    pub struct MirrorReq {
    }
    #[derive(Debug)]
    pub struct SqlReq {
    }
}

use types::* ;
#[async_trait]
pub trait MinioClient {
    async fn mb(&self, req: MbReq) -> anyhow::Result<Value> ;
    async fn ls(&self, req: LsReq) -> anyhow::Result<Value> ;
    async fn cp(&self, req: CpReq) -> anyhow::Result<Value> ;
    async fn cat(&self, req: CatReq) -> anyhow::Result<Value> ;
    async fn tee(&self, req: TeeReq) -> anyhow::Result<Value> ;
    async fn rm(&self, req: RmReq) -> anyhow::Result<Value> ;
    async fn share(&self, req: ShareReq) -> anyhow::Result<Value> ;
    async fn mirror(&self, req: MirrorReq) -> anyhow::Result<Value> ;
    async fn sql(&self, req: SqlReq) -> anyhow::Result<Value> ;
}

pub struct MinioPlugin {
    pub new_client: fn(db: Db, paths: Vec<String>) -> Pin<Box<dyn std::future::Future<Output = Option<anyhow::Result<Box<dyn MinioClient>>>>>>,
}

inventory::collect!(MinioPlugin) ;
pub async fn new_client(db: &Db, paths: Vec<String>) -> anyhow::Result<Box<dyn MinioClient>>{
    log::debug!("loading minio plugin...") ;
    for plugin in inventory::iter::<MinioPlugin>.into_iter() {
        let plugin_result = (plugin.new_client)(db.clone(), paths.clone()).await;
        if plugin_result.is_none() { continue };
        let plugin_result = plugin_result.unwrap() ;
        return plugin_result
    }
    Err(anyhow::anyhow!("PLUGIN NOT FIND: [plugin_type].[plugin_id]/[plugin_path]"))
}
